Update(zen-go): add claude-opus-4-8, minimax-m3, mimo-v2.5-free models and proper effort level integration for Zen/Go models - #1505
Conversation
Add OpenCode as a first-class provider, enabling users to connect their Zen (pay-as-you-go) and Go ($10/mo) subscriptions via the /provider command. New integration descriptors: - vendors/opencode.ts — OpenCode Zen vendor (41 models) - gateways/opencode-go.ts — OpenCode Go gateway (12 models) - brands/opencode.ts — brand descriptor - models/opencode.ts — full model catalog (GPT, Claude, Gemini, Qwen, GLM, Kimi, MiniMax, Grok, DeepSeek, MiMo, Nemotron) Modified files: - integrationArtifacts.generated.ts — register descriptors and presets - providerProfile.ts — add OPENCODE_API_KEY env/secret key, 'opencode' profile type, and buildLaunchEnv handler - providerConfig.ts — add DEFAULT_OPENCODE_BASE_URL constants Auth: OPENCODE_API_KEY env var or interactive key entry in /provider Transport: openai-compatible (chat_completions) Base URLs: https://opencode.ai/zen/v1 (Zen), /zen/go/v1 (Go)
Add visual tags in the /provider preset selection to distinguish OpenCode Zen (pay-as-you-go) from OpenCode Go (subscription).
Switch OpenCode vendor and Go gateway from static to hybrid model catalog with openai-compatible discovery. Models are fetched from /v1/models on startup and cached for 1 hour. Manual refresh is supported via the /provider UI. Static model list is preserved as fallback when discovery fails.
97 tests across 2 files covering:
Integration tests (72 tests):
- Vendor descriptor: id, label, classification, base URL, model, auth,
transport, preset, validation, catalog, discovery, usage metadata
- Gateway descriptor: id, label, vendorId, category, base URL, model,
auth, transport, preset, catalog, discovery
- Brand descriptor: id, label, canonicalVendorId, capabilities, modelIds
- Model catalog: registration, vendor/gateway associations, required
fields, valid classifications, reasoning/coding tags, no duplicates,
model counts (41 Zen, 12 Go), modelDescriptorId consistency
- Cross-reference: brand↔model, vendor↔model, gateway↔model,
shared OPENCODE_API_KEY
- Registry validation: no errors, no preset conflicts
- Edge cases: unique ids, unique apiNames, non-empty labels, valid
contextWindow/maxOutputTokens, valid defaultModel format, validation
message content, discovery config
Profile tests (25 tests):
- Type guard: isProviderProfile('opencode'), rejects invalid values
- buildLaunchEnv: persisted env, defaults, process env precedence,
OPENCODE_API_KEY mapping, whitespace/null/undefined/empty handling,
very long keys, special characters, concurrent access, boundary
values, no credential leakage
Add endpointPath field to OpenAIShimTransportConfig so catalog entries can specify which API path to use per model. This addresses the maintainer's [P1] finding that all models were routed to /chat/completions regardless of their upstream endpoint. Changes: - descriptors.ts: add endpointPath?: string to OpenAIShimTransportConfig - openaiShim.ts: buildRequestUrl checks shimConfig.endpointPath first - vendors/opencode.ts: add transportOverrides to 31 catalog entries (GPT→/responses, Claude/Qwen→/messages, Gemini→/models/<id>) + switch to source: 'static' to prevent free models from live API - gateways/opencode-go.ts: add transportOverrides to 4 entries (MiniMax/Qwen→/messages) + switch to source: 'static' - opencode.test.ts: update tests for static source, remove discovery tests
…scriptors - Add OpenCode Zen/Go rows to README supported providers table - Add OpenCode Zen/Go examples and OPENCODE_API_KEY to advanced-setup.md - Add PresetBadge type to descriptor/manifest with badge propagation in artifact generator - Move 4 hard-coded preset badges ([FREE], [Sponsor], [Zen], [Go]) from ProviderManager.tsx into descriptor preset metadata - Add badge field to providerUiMetadata so UI components read from manifest - Update integration overview docs to recommend preset.badge for future gateways
…ssages and /responses (P1) Extend the openaiShim transport so that endpointPath overrides select both the URL and the correct body/response format: - /responses → OpenAI Responses API body (input, max_output_tokens) - /messages → Anthropic Messages API body (content blocks, system, max_tokens) Also fixes: abort listener leak in SSE passthrough, system prompt content-block flattening, and removes [Zen]/[Go] badge entries (P3). Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
…n Gemini models (P1) The three Gemini models in the OpenCode Zen catalog (gemini-3.5-flash, gemini-3.1-pro, gemini-3-flash) were sending chat-completions body to the /models/gemini-* endpoint, which expects Google AI SDK format. - effectiveTransport now detects /models/gemini- endpointPath → 'gemini' - buildGeminiBody() converts Anthropic messages → Google contents[] with role mapping, systemInstruction, generationConfig, functionDeclarations - geminiSseToAnthropic() parses Google SSE frames → Anthropic stream events with text deltas, functionCall tool_use, finishReason mapping - _convertGeminiToAnthropicResponse() for non-streaming responses - Streaming/non-streaming routing via URL detection (/models/gemini-) - serializeBody(), hasToolsPayload, omitGeminiTools all updated
P1: Prefix all defaultModel values in opencode.ts with 'opencode-' so the fallback findModelDescriptorForApiName() doesn't match canonical model names. The OpenCode descriptors are still found via catalog entry lookup when the OpenCode route is active. P2: Add 'OpenCode Go' and 'OpenCode Zen' to PRESET_ORDER in ProviderManager.test.tsx between 'OpenAI' and 'OpenRouter' so navigateToPreset() sends the correct number of j keypresses.
- category: 'hosted' → 'aggregating' (both are aggregating gateways) - add validation block with OPENCODE_API_KEY guidance - update test assertion from 'hosted' to 'aggregating'
- fix(autocompact): retry circuit breaker after cooldown (Twigpine#1375) - fix(provider): require API key input when adding OpenGateway (Twigpine#1384) - fix(provider): allow remote Ollama without OPENAI_API_KEY (Twigpine#952) - fix(codex-stream): recover tool args delivered only via done events (Twigpine#1262) - fix: route MiniMax compacting through Anthropic-compatible API (Twigpine#1154) - fix(thinking): disable thinking for unsupported Ollama models (Twigpine#1376) - feat(agents): set active session agent from agents menu (Twigpine#1349) - fix(repl): show permission prompts while draft input is present (Twigpine#1393) - fix(model): include profile models in descriptor picker (Twigpine#1361) - Improve warning notice formatting (Twigpine#1415) - fix(codex): allow credential storage fallback (Twigpine#1347) - fix(attribution): make git attribution opt-in by default (Twigpine#1335) - fix(agent): allow custom model overrides (Twigpine#1337) - feat(query): robust multi-lingual and structural continuation nudge (Twigpine#1280) - fix(watchers): debounce skills and settings reload bursts (Twigpine#1370) - feat: configure API retry backoff (Twigpine#370) (Twigpine#1095) - chore(main): release 0.15.0 (Twigpine#1325) - ci: retrigger CodeQL after action download outage (Twigpine#1374) - Fix launcher heap setup for long sessions (Twigpine#1242)
When users set up OpenCode Zen/Go via /provider, the key is saved as OPENAI_API_KEY (via buildCompatibilityProcessEnv). The validation block only checked OPENCODE_API_KEY, causing a startup warning even though the runtime auth header had the key it needed. Add OPENAI_API_KEY to validation.credentialEnvVars for both gateways, matching the pattern used by Hicap and Gitlawb Opengateway.
# Conflicts: # README.md # src/components/ProviderManager.tsx # src/integrations/gateways/gitlawb-opengateway.ts # src/integrations/generated/integrationArtifacts.generated.ts # src/utils/providerProfile.ts
- buildResponsesBody: add reasoning_effort + reasoning_summary + include - buildAnthropicMessagesBody: add thinking config (adaptive/enabled/budget) - buildGeminiBody: add thinkingConfig with thinkingLevel mapping - modelSupportsEffort: allow OpenCode Claude and Gemini models - modelSupportsMaxEffort: add opus-4-7 - getAvailableEffortLevels: show standard levels for OpenCode native models - opencode-go: add missing validation block
…hance effort level handling
…ffort level handling
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📜 Recent review details🧰 Additional context used📓 Path-based instructions (3)**/*.{ts,tsx,js,jsx,py}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*⚙️ CodeRabbit configuration file
Files:
**⚙️ CodeRabbit configuration file
Files:
🔇 Additional comments (1)
📝 WalkthroughWalkthroughThis PR introduces ChangesEffort Levels and Model Expansion
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 5 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/advanced-setup.md`:
- Around line 197-199: Update the OpenCode Go model count in
docs/advanced-setup.md so the sentence that currently reads “12 open models”
matches the rest of the PR (change to “13 open models”); locate the OpenCode Go
description near the OpenCode Zen paragraph and replace the numeric count text
for OpenCode Go to 13 to keep the section consistent.
In `@src/services/api/openaiShim.ts`:
- Around line 2291-2307: The adaptive-model detection misses opus 4.8, so update
the isAdaptive check (the variable computed from modelLower near
modelLower.includes(...) and the related isOpus45 branch) to include the 4.8
variants (e.g., 'opus-4-8' and 'opus-4.8' and, if applicable,
'sonnet-4-8'/'sonnet-4.8') so that for claude-opus-4-8 the path that sets
anthropicBody.thinking = { type: 'adaptive' } and anthropicBody.effort = effort
is used instead of falling back to the budgetTokens branch; keep the existing
isOpus45 logic unchanged.
In `@src/utils/effort.ts`:
- Around line 97-99: The current early return in modelUsesOpenAIEffort only
checks provider and thus treats native OpenCode/gemini routes as OpenAI-style,
enabling xhigh incorrectly; update modelUsesOpenAIEffort to require both
provider === 'openai' AND an explicit marker that the model implements
OpenAI/chat-style semantics (for example a capability flag like supportsChat or
apiStyle === 'openai' / family startsWith('gpt-')), explicitly excluding native
OpenCode routes (e.g., models flagged as open_code or route type 'messages' that
are not OpenAI-compatible); ensure getAvailableEffortLevels and
modelSupportsXHighEffort call the revised modelUsesOpenAIEffort so
resolveAppliedEffort’s clamping behaves correctly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d374ad16-bfea-46fa-b734-9687d5cff4cb
⛔ Files ignored due to path filters (1)
src/integrations/generated/integrationArtifacts.generated.tsis excluded by!**/generated/**
📒 Files selected for processing (10)
README.mddocs/advanced-setup.mdsrc/entrypoints/sdk/runtimeTypes.tssrc/integrations/gateways/opencode-go.tssrc/integrations/gateways/opencode.test.tssrc/integrations/gateways/opencode.tssrc/integrations/models/opencode.tssrc/services/api/openaiShim.tssrc/utils/effort.codex.test.tssrc/utils/effort.ts
- docs/advanced-setup.md: bump OpenCode Go count 12 → 13 - openaiShim.ts: include opus-4-8 / opus-4.8 in the adaptive thinking detection so the new model uses the adaptive + effort path instead of falling back to budgetTokens - effort.ts: modelUsesOpenAIEffort now also rejects models that include 'claude-' or 'gemini-' — without this, OpenCode Claude/Gemini routes (provider=openai) were misclassified as OpenAI-style and could leak xhigh past the new gate - effort.codex.test.ts: lock in the new exclusion with a regression test against the openai provider Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jatmn
left a comment
There was a problem hiding this comment.
I found a few issues that need to be addressed before this is ready.
Findings
-
[P2] Accept
xhighin the persisted settings schema before writing it
src/utils/effort.ts:202
toPersistableEffort()now returnsxhigh, and the/effort, effort callout, and model picker paths all write that value back touserSettings.effortLevel. The settings schema still only acceptslow | medium | high | max, so the changed callers now hit TypeScript errors and a persistedxhighsetting will be rejected/parsed away instead of surviving restart. Please update the settings contract anywhereeffortLevelis validated or inferred before makingxhighpersistable. -
[P2] Include
xhighin the model picker effort cycle for supported models
src/components/ModelPicker.tsx:445
The/modelpicker still decides the available cycle from a booleanincludeMax, so even onclaude-opus-4-8the arrow controls only cycle throughlow,medium,high, andmax. This leaves the new model unable to select thexhighlevel from the model switch flow even though the PR adds it specifically for that model. Please drive this picker from the same available-level helper used by/effort, or add an explicit xhigh-capable branch. -
[P2] Keep SDK/control effort metadata in sync with the new level
src/cli/print.ts:1213
The SDK/control model info still buildssupportedEffortLevelsfrommodelSupportsMaxEffort() ? [...EFFORT_LEVELS] : ..., andEFFORT_LEVELSnow includesxhigh. That over-advertisesxhighfor max-capable models that do not support it, such asopus-4-6, while the generatedModelInfoSchema/types still rejectxhighentirely. The PR typecheck now fails at the initialize response withsupportedEffortLevelscontainingxhigh. Please use the model-specific available-level logic here and regenerate/update the SDK schema/types so control clients see the same contract as the CLI.
Working on it.... |
Closes the three P2 findings from PR Twigpine#1505 review: 1. Settings schema now accepts 'xhigh' so a persisted xhigh survives restart instead of being silently dropped by .catch(undefined). 2. ModelPicker /effort cycle is driven by getAvailableEffortLevels(model) instead of a boolean includeMax, so models supporting xhigh (opus-4-7/4-8, OpenAI/Codex) can actually select it from the picker. displayEffort clamp now uses the available levels list, so stale xhigh also clamps to high when the focused model doesn't support it. 3. SDK/control metadata uses getAvailableEffortLevels(model) instead of the EFFORT_LEVELS fallback that advertised xhigh to every max-capable model. SDK schema + generated types extended to include 'xhigh'. Also fixes a latent generator bug: the array case in generate-sdk-types now parenthesizes union/intersection elements so the trailing [] binds the whole type, e.g. ("a"|"b")[] rather than "a"|"b[]. Without this, the regenerated xhigh levels ended up typed as the single-literal "xhigh"[] and broke the modelInfo assignability check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EFFORT_LEVELS now matches getAvailableEffortLevels() output order (['low', 'medium', 'high', 'xhigh', 'max']), and the order asserted by the existing effort.codex.test.ts tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Matches the EFFORT_LEVELS / getAvailableEffortLevels order from the previous commit. The Zod enum order doesn't affect runtime validation, but keeps the source consistent and avoids confusion if anyone reads the enum literal to infer display order. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Pushed 3 new commits on top of the previous PR tip to address the three [P2] findings from the review:
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/components/ModelPicker.tsx (2)
184-197:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winClamp the selected effort, not just the rendered label.
Lines 184-197 only normalize what the picker displays. If a user picks
xhigh, moves focus to a model whosefocusedAvailableLevelsno longer include it, and presses Enter,handleSelectstill persists and emits the raweffortfrom Lines 254-267. Runtime clamping hides the API error later, but the picker itself reports a value the focused model does not support.Also applies to: 248-267
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ModelPicker.tsx` around lines 184 - 197, The picker currently only clamps the rendered label (displayEffort) when the focused model doesn't support the current effort, but leaves the actual selected value unchanged; update the logic so the selected/committed effort is clamped as well: when computing focusedAvailableLevels (via resolveOptionModel and getAvailableEffortLevels) and focusedDefaultEffort (via getDefaultEffortLevelForOption), ensure the variable used by handleSelect is replaced or normalized to a supported value (use focusedDefaultEffort or the nearest available level) instead of the raw effort; adjust the code paths around displayEffort and the selection handler (handleSelect) so both display and persisted/emailed effort values are determined from the clamped value rather than the original effort variable.
170-187:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRe-key effort cycling to include
focusedAvailableLevels(and align selection/persistence with supported levels).
handleCycleEffortis only regenerated whenfocusedDefaultEffort,focusedSupportsEffort, andfocusedSupportsMaxchange, but it closes overfocusedAvailableLevels; when switching between models where onlyxhighavailability differs, cycling can use a stale level set (makingxhighunreachable or causing incorrect cycling).- UI clamps the displayed effort to
"high"when unsupported, buthandleSelect/persistence emits the raweffort(includingxhigh) without verifying it’s in the focused model’sgetAvailableEffortLevels, creating a display-vs-selection mismatch.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ModelPicker.tsx` around lines 170 - 187, The handler generation and selection logic close over a stale set of available effort levels: update the code so handleCycleEffort is re-created when focusedAvailableLevels changes (add focusedAvailableLevels to its dependency list alongside focusedDefaultEffort, focusedSupportsEffort and focusedSupportsMax) and adjust handleSelect to validate/clamp the chosen effort against getAvailableEffortLevels(resolveOptionModel(focusedValue)) before emitting/persisting it (so displayed/clamped values like "high" and persisted values are always from focusedAvailableLevels); also ensure any places using focusedSupportsEffort/focusedSupportsMax still derive those from resolveOptionModel(focusedValue) consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/components/ModelPicker.tsx`:
- Around line 184-197: The picker currently only clamps the rendered label
(displayEffort) when the focused model doesn't support the current effort, but
leaves the actual selected value unchanged; update the logic so the
selected/committed effort is clamped as well: when computing
focusedAvailableLevels (via resolveOptionModel and getAvailableEffortLevels) and
focusedDefaultEffort (via getDefaultEffortLevelForOption), ensure the variable
used by handleSelect is replaced or normalized to a supported value (use
focusedDefaultEffort or the nearest available level) instead of the raw effort;
adjust the code paths around displayEffort and the selection handler
(handleSelect) so both display and persisted/emailed effort values are
determined from the clamped value rather than the original effort variable.
- Around line 170-187: The handler generation and selection logic close over a
stale set of available effort levels: update the code so handleCycleEffort is
re-created when focusedAvailableLevels changes (add focusedAvailableLevels to
its dependency list alongside focusedDefaultEffort, focusedSupportsEffort and
focusedSupportsMax) and adjust handleSelect to validate/clamp the chosen effort
against getAvailableEffortLevels(resolveOptionModel(focusedValue)) before
emitting/persisting it (so displayed/clamped values like "high" and persisted
values are always from focusedAvailableLevels); also ensure any places using
focusedSupportsEffort/focusedSupportsMax still derive those from
resolveOptionModel(focusedValue) consistently.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d324d1cc-2fd3-42ed-a424-d54ce21ca358
📒 Files selected for processing (7)
scripts/generate-sdk-types.tssrc/cli/print.tssrc/components/ModelPicker.tsxsrc/entrypoints/sdk/coreSchemas.tssrc/entrypoints/sdk/coreTypes.generated.tssrc/utils/effort.tssrc/utils/settings/types.ts
✅ Files skipped from review due to trivial changes (1)
- src/entrypoints/sdk/coreTypes.generated.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/utils/effort.ts
|
Addressed both reviewer findings from @jatmn (CodeRabbit) in commit 7d65569: P2 — narrow the effort allowlist: Collapsed the two 4-model branches in P3 — sync max description: Updated New test in All 12 effort codex tests pass. |
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the update. I rechecked the previously discussed paths and do not see any remaining actionable issues from my side.
…o-integration # Conflicts: # scripts/generate-sdk-types.ts # src/entrypoints/sdk/coreTypes.generated.ts
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@README.md`:
- Around line 42-46: The PR description and AI summary do not mention the Atlas
Cloud sponsor entry that was added alongside the model catalog update; update
the PR description to explicitly list the sponsor addition (the "Atlas Cloud"
banner/sponsor entry in README) as one of the PR objectives and include a short
note in the AI summary so the commit history and reviewers can see this doc-only
change.
- Around line 213-217: The PR description is missing mention of the agent
routing example change: update the PR objectives/AI summary to note that the
agent routing example now uses the "zai-default" entry and that the example
documents the "model" override behavior (i.e., that per-agent "model" can differ
from the catalog default); explicitly reference the keys shown ("zai-default",
"model", "base_url", "api_key") and add a short sentence in the PR description
summarizing these changes and that this update is bundled with the model catalog
update (also ensure similar note is added where the README lines around the
other occurrences are referenced).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 38abfe89-2b4c-47f3-91bb-c73fac02de57
⛔ Files ignored due to path filters (1)
src/entrypoints/sdk/coreTypes.generated.tsis excluded by!src/entrypoints/sdk/coreTypes.generated.ts
📒 Files selected for processing (5)
README.mdsrc/entrypoints/sdk/coreSchemas.tssrc/main.tsxsrc/services/api/openaiShim.tssrc/utils/settings/types.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*
⚙️ CodeRabbit configuration file
**/*: Apply the OpenClaude maintainer review rubric from AGENTS.md. Review the current diff, not stale discussion context. Separate real blockers from suggestions. Do not request changes for vague style churn. Treat approval as merge-ready from CodeRabbit's side, pending required human review and GitHub Checks. If checks are failing or unavailable, say so clearly instead of implying the PR is fully ready.
Files:
README.mdsrc/main.tsxsrc/utils/settings/types.tssrc/entrypoints/sdk/coreSchemas.tssrc/services/api/openaiShim.ts
{README.md,CONTRIBUTING.md,docs/**,.github/pull_request_template.md}
⚙️ CodeRabbit configuration file
{README.md,CONTRIBUTING.md,docs/**,.github/pull_request_template.md}: Review docs for accuracy against current code behavior. Flag security or provider claims that overpromise, stale install commands, missing setup caveats, and instructions that could push users toward unsafe credential handling. Keep purely wording-level suggestions non-blocking.
Files:
README.md
**
⚙️ CodeRabbit configuration file
**: # Contributing to OpenClaudeThanks for contributing.
OpenClaude is a fast-moving open-source coding-agent CLI with support for multiple providers, local backends, MCP, and a terminal-first workflow. The best contributions here are focused, well-tested, and easy to review.
Before You Start
- Search existing issues and discussions before opening a new thread.
- Check open pull requests for work that overlaps with your contribution. If a PR already exists that addresses the same change, open an issue or discussion first to align on direction — duplicate PRs may be closed without review.
- Use issues for confirmed bugs and actionable feature work.
- Use discussions for setup help, ideas, and general community conversation.
- For larger changes, open an issue first so the scope is clear before implementation.
- For security reports, follow SECURITY.md.
Pull Requests
Every PR needs a reason. Your PR description must include:
- what changed and why
- the user or developer impact
- the exact checks you ran
- a linked issue when one exists, using
Fixes#123, `Closes `#123, or another clear link- screenshots when the PR touches UI, terminal presentation, or the VS Code extension
- which provider path was tested when the PR changes provider behavior
The PR author is responsible for ensuring their PR is merge-ready. PRs with merge conflicts will not be reviewed or approved until the conflicts are resolved.
Issues are the recommended starting point for anything non-trivial — opening one first helps avoid wasted effort if the change is out of scope or already being worked on. Small fixes, doc corrections, and obvious improvements can stand on their own without a linked issue, as long as the PR description explains the intent.
What Gets Closed Without Review
PRs may be closed without review...
Files:
README.mdsrc/main.tsxsrc/utils/settings/types.tssrc/entrypoints/sdk/coreSchemas.tssrc/services/api/openaiShim.ts
{bin/**,scripts/**,package.json,src/setup.ts,src/main.tsx,src/entrypoints/**}
⚙️ CodeRabbit configuration file
{bin/**,scripts/**,package.json,src/setup.ts,src/main.tsx,src/entrypoints/**}: Review install, launcher, build, packaging, startup, and entrypoint changes for cross-platform compatibility, tracked-source rewrites, env/config precedence, and release safety. Block on changes that can break Windows/macOS/Linux startup or publish unexpected artifacts.
Files:
src/main.tsxsrc/entrypoints/sdk/coreSchemas.ts
src/{components/permissions,utils/permissions,hooks/toolPermission,tools,entrypoints/sdk}/**
⚙️ CodeRabbit configuration file
src/{components/permissions,utils/permissions,hooks/toolPermission,tools,entrypoints/sdk}/**: Review permission prompts, auto-allow logic, sandbox behavior, SDK permission schemas, shell/PowerShell execution, and background execution paths as security-sensitive. Block on bypasses, unclear trust boundaries, unsafe path handling, missing user visibility, or changes that broaden allowed behavior without an explicit maintainer decision.
Files:
src/entrypoints/sdk/coreSchemas.ts
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}
⚙️ CodeRabbit configuration file
{src/services/api/**,src/integrations/**,src/utils/model/**,src/utils/provider*.ts,src/commands/provider/**}: Review provider routing, model selection, env precedence, auth/token handling, OpenAI-compatible shims, retries, proxy behavior, and outbound HTTP behavior with high scrutiny. Block on silent default changes, hidden fallback expansion, credential reuse mistakes, hardcoded provider assumptions, or new network reach that is not intentional and documented.
Files:
src/services/api/openaiShim.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Gitlawb/openclaude PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-06-04T22:10:40.834Z
Learning: Verify that product, trust-model, routing-default, telemetry/network, and permission-policy changes are not hidden inside unrelated cleanup. Flag the PR if the policy decision needs explicit maintainer alignment.
🔇 Additional comments (5)
src/entrypoints/sdk/coreSchemas.ts (1)
65-79: LGTM!Also applies to: 1114-1115
src/utils/settings/types.ts (1)
740-742: LGTM!Also applies to: 753-766
src/services/api/openaiShim.ts (1)
1927-1927: LGTM!Also applies to: 2200-2200, 2215-2215, 2293-2297, 2465-2467, 2549-2549, 2576-2578, 2640-2643, 2877-2879
src/main.tsx (1)
226-228: LGTM!Also applies to: 271-272, 351-357, 373-374, 381-382, 735-737, 954-961, 3050-3050
README.md (1)
171-172: LGTM!
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@README.md`:
- Around line 42-46: The PR description and AI summary do not mention the Atlas
Cloud sponsor entry that was added alongside the model catalog update; update
the PR description to explicitly list the sponsor addition (the "Atlas Cloud"
banner/sponsor entry in README) as one of the PR objectives and include a short
note in the AI summary so the commit history and reviewers can see this doc-only
change.
- Around line 213-217: The PR description is missing mention of the agent
routing example change: update the PR objectives/AI summary to note that the
agent routing example now uses the "zai-default" entry and that the example
documents the "model" override behavior (i.e., that per-agent "model" can differ
from the catalog default); explicitly reference the keys shown ("zai-default",
"model", "base_url", "api_key") and add a short sentence in the PR description
summarizing these changes and that this update is bundled with the model catalog
update (also ensure similar note is added where the README lines around the
other occurrences are referenced).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 38abfe89-2b4c-47f3-91bb-c73fac02de57
⛔ Files ignored due to path filters (1)
src/entrypoints/sdk/coreTypes.generated.tsis excluded by!src/entrypoints/sdk/coreTypes.generated.ts
📒 Files selected for processing (5)
README.mdsrc/entrypoints/sdk/coreSchemas.tssrc/main.tsxsrc/services/api/openaiShim.tssrc/utils/settings/types.ts
📜 Review details
🔇 Additional comments (5)
src/entrypoints/sdk/coreSchemas.ts (1)
65-79: LGTM!Also applies to: 1114-1115
src/utils/settings/types.ts (1)
740-742: LGTM!Also applies to: 753-766
src/services/api/openaiShim.ts (1)
1927-1927: LGTM!Also applies to: 2200-2200, 2215-2215, 2293-2297, 2465-2467, 2549-2549, 2576-2578, 2640-2643, 2877-2879
src/main.tsx (1)
226-228: LGTM!Also applies to: 271-272, 351-357, 373-374, 381-382, 735-737, 954-961, 3050-3050
README.md (1)
171-172: LGTM!
🛑 Comments failed to post (2)
README.md (2)
42-46: 🧹 Nitpick | 🔵 Trivial
Document the sponsor addition in the PR description.
The Atlas Cloud sponsor entry is bundled with the model catalog update but not mentioned in the PR objectives or AI summary. For commit history clarity, the PR description should list all changes, even when they are doc-only.
Also applies to: 53-53
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@README.md` around lines 42 - 46, The PR description and AI summary do not mention the Atlas Cloud sponsor entry that was added alongside the model catalog update; update the PR description to explicitly list the sponsor addition (the "Atlas Cloud" banner/sponsor entry in README) as one of the PR objectives and include a short note in the AI summary so the commit history and reviewers can see this doc-only change.
213-217: 🧹 Nitpick | 🔵 Trivial
Document the agent routing example update in the PR description.
The agent routing example was updated to use
zai-defaultand includes additional explanation of themodeloverride behavior. This change is bundled with the model catalog update but not mentioned in the PR objectives or AI summary.Also applies to: 227-227, 235-235
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@README.md` around lines 213 - 217, The PR description is missing mention of the agent routing example change: update the PR objectives/AI summary to note that the agent routing example now uses the "zai-default" entry and that the example documents the "model" override behavior (i.e., that per-agent "model" can differ from the catalog default); explicitly reference the keys shown ("zai-default", "model", "base_url", "api_key") and add a short sentence in the PR description summarizing these changes and that this update is bundled with the model catalog update (also ensure similar note is added where the README lines around the other occurrences are referenced).
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the update. I rechecked the previously discussed effort-routing paths and found one issue that still needs to be addressed.
Findings
- [P2] Complete the xhigh gate for non-effort OpenCode models
src/utils/effort.ts:100
CodeRabbit's earlier request to keepxhighscoped to models that actually support it is marked addressed, but the current guard still returns true for every model thatmodelUsesOpenAIEffort()classifies as OpenAI-shaped. That leaves models such asopencode-go-minimax-m3andmimo-v2.5-freewithmodelSupportsEffort() === falseand no picker effort levels, whileresolveAppliedEffort(model, 'xhigh')still returnsxhighinstead of clamping. From theregetAnthropicClient()turns the value intorequest.reasoning.effort, and the/messagesshim mapsxhightomax/thinking controls for non-adaptive models, so a stalesettings.jsonvalue or explicit--effort xhighcan still send unsupported effort fields to the newly added OpenCode Go MiniMax path and other non-effort OpenAI-compatible models. Please complete that gating fix by requiring actual effort/xhigh support for this branch, or by makingresolveAppliedEffort()clamp anyxhighvalue when the target model does not support effort.
|
Addressed: xhigh gate now requires modelSupportsEffort
Fix: added |
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the update. I rechecked the previously discussed paths and do not see any remaining actionable issues from my side.
@kevincodex1 LGT
kevincodex1
left a comment
There was a problem hiding this comment.
Looks great! thank you for this
…s and proper effort level integration for Zen/Go models (Twigpine#1505) * feat(provider): add OpenCode Zen/Go subscription support Add OpenCode as a first-class provider, enabling users to connect their Zen (pay-as-you-go) and Go ($10/mo) subscriptions via the /provider command. New integration descriptors: - vendors/opencode.ts — OpenCode Zen vendor (41 models) - gateways/opencode-go.ts — OpenCode Go gateway (12 models) - brands/opencode.ts — brand descriptor - models/opencode.ts — full model catalog (GPT, Claude, Gemini, Qwen, GLM, Kimi, MiniMax, Grok, DeepSeek, MiMo, Nemotron) Modified files: - integrationArtifacts.generated.ts — register descriptors and presets - providerProfile.ts — add OPENCODE_API_KEY env/secret key, 'opencode' profile type, and buildLaunchEnv handler - providerConfig.ts — add DEFAULT_OPENCODE_BASE_URL constants Auth: OPENCODE_API_KEY env var or interactive key entry in /provider Transport: openai-compatible (chat_completions) Base URLs: https://opencode.ai/zen/v1 (Zen), /zen/go/v1 (Go) * feat(provider): add [Zen]/[Go] tags to OpenCode preset labels Add visual tags in the /provider preset selection to distinguish OpenCode Zen (pay-as-you-go) from OpenCode Go (subscription). * feat(provider): enable dynamic model discovery for OpenCode Switch OpenCode vendor and Go gateway from static to hybrid model catalog with openai-compatible discovery. Models are fetched from /v1/models on startup and cached for 1 hour. Manual refresh is supported via the /provider UI. Static model list is preserved as fallback when discovery fails. * test(provider): add comprehensive OpenCode Zen/Go test suite 97 tests across 2 files covering: Integration tests (72 tests): - Vendor descriptor: id, label, classification, base URL, model, auth, transport, preset, validation, catalog, discovery, usage metadata - Gateway descriptor: id, label, vendorId, category, base URL, model, auth, transport, preset, catalog, discovery - Brand descriptor: id, label, canonicalVendorId, capabilities, modelIds - Model catalog: registration, vendor/gateway associations, required fields, valid classifications, reasoning/coding tags, no duplicates, model counts (41 Zen, 12 Go), modelDescriptorId consistency - Cross-reference: brand↔model, vendor↔model, gateway↔model, shared OPENCODE_API_KEY - Registry validation: no errors, no preset conflicts - Edge cases: unique ids, unique apiNames, non-empty labels, valid contextWindow/maxOutputTokens, valid defaultModel format, validation message content, discovery config Profile tests (25 tests): - Type guard: isProviderProfile('opencode'), rejects invalid values - buildLaunchEnv: persisted env, defaults, process env precedence, OPENCODE_API_KEY mapping, whitespace/null/undefined/empty handling, very long keys, special characters, concurrent access, boundary values, no credential leakage * fix(provider): add per-model endpoint routing (P1) Add endpointPath field to OpenAIShimTransportConfig so catalog entries can specify which API path to use per model. This addresses the maintainer's [P1] finding that all models were routed to /chat/completions regardless of their upstream endpoint. Changes: - descriptors.ts: add endpointPath?: string to OpenAIShimTransportConfig - openaiShim.ts: buildRequestUrl checks shimConfig.endpointPath first - vendors/opencode.ts: add transportOverrides to 31 catalog entries (GPT→/responses, Claude/Qwen→/messages, Gemini→/models/<id>) + switch to source: 'static' to prevent free models from live API - gateways/opencode-go.ts: add transportOverrides to 4 entries (MiniMax/Qwen→/messages) + switch to source: 'static' - opencode.test.ts: update tests for static source, remove discovery tests * refactor(opencode): model OpenCode Zen/Go as gateways (P2) * docs(provider): document OpenCode setup and move badge metadata to descriptors - Add OpenCode Zen/Go rows to README supported providers table - Add OpenCode Zen/Go examples and OPENCODE_API_KEY to advanced-setup.md - Add PresetBadge type to descriptor/manifest with badge propagation in artifact generator - Move 4 hard-coded preset badges ([FREE], [Sponsor], [Zen], [Go]) from ProviderManager.tsx into descriptor preset metadata - Add badge field to providerUiMetadata so UI components read from manifest - Update integration overview docs to recommend preset.badge for future gateways * fix(provider): match request body to endpoint format for OpenCode /messages and /responses (P1) Extend the openaiShim transport so that endpointPath overrides select both the URL and the correct body/response format: - /responses → OpenAI Responses API body (input, max_output_tokens) - /messages → Anthropic Messages API body (content blocks, system, max_tokens) Also fixes: abort listener leak in SSE passthrough, system prompt content-block flattening, and removes [Zen]/[Go] badge entries (P3). Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com> * fix(provider): add Google AI SDK body/response format for OpenCode Zen Gemini models (P1) The three Gemini models in the OpenCode Zen catalog (gemini-3.5-flash, gemini-3.1-pro, gemini-3-flash) were sending chat-completions body to the /models/gemini-* endpoint, which expects Google AI SDK format. - effectiveTransport now detects /models/gemini- endpointPath → 'gemini' - buildGeminiBody() converts Anthropic messages → Google contents[] with role mapping, systemInstruction, generationConfig, functionDeclarations - geminiSseToAnthropic() parses Google SSE frames → Anthropic stream events with text deltas, functionCall tool_use, finishReason mapping - _convertGeminiToAnthropicResponse() for non-streaming responses - Streaming/non-streaming routing via URL detection (/models/gemini-) - serializeBody(), hasToolsPayload, omitGeminiTools all updated * fix: prevent OpenCode model descriptors from shadowing canonical limits P1: Prefix all defaultModel values in opencode.ts with 'opencode-' so the fallback findModelDescriptorForApiName() doesn't match canonical model names. The OpenCode descriptors are still found via catalog entry lookup when the OpenCode route is active. P2: Add 'OpenCode Go' and 'OpenCode Zen' to PRESET_ORDER in ProviderManager.test.tsx between 'OpenAI' and 'OpenRouter' so navigateToPreset() sends the correct number of j keypresses. * fix: align OpenCode Go descriptor metadata with Zen - category: 'hosted' → 'aggregating' (both are aggregating gateways) - add validation block with OPENCODE_API_KEY guidance - update test assertion from 'hosted' to 'aggregating' * fix: accept OPENAI_API_KEY as fallback in OpenCode validation When users set up OpenCode Zen/Go via /provider, the key is saved as OPENAI_API_KEY (via buildCompatibilityProcessEnv). The validation block only checked OPENCODE_API_KEY, causing a startup warning even though the runtime auth header had the key it needed. Add OPENAI_API_KEY to validation.credentialEnvVars for both gateways, matching the pattern used by Hicap and Gitlawb Opengateway. * chore: trigger mergeability recheck * feat(shim): forward effort/thinking to OpenCode Zen/Go endpoints - buildResponsesBody: add reasoning_effort + reasoning_summary + include - buildAnthropicMessagesBody: add thinking config (adaptive/enabled/budget) - buildGeminiBody: add thinkingConfig with thinkingLevel mapping - modelSupportsEffort: allow OpenCode Claude and Gemini models - modelSupportsMaxEffort: add opus-4-7 - getAvailableEffortLevels: show standard levels for OpenCode native models - opencode-go: add missing validation block * feat: update OpenCode Zen and Go model counts, add new models, and enhance effort level handling * feat: implement xhigh effort support for specific models and adjust effort level handling * fix(effort): address reviewer feedback on xhigh + new models - docs/advanced-setup.md: bump OpenCode Go count 12 → 13 - openaiShim.ts: include opus-4-8 / opus-4.8 in the adaptive thinking detection so the new model uses the adaptive + effort path instead of falling back to budgetTokens - effort.ts: modelUsesOpenAIEffort now also rejects models that include 'claude-' or 'gemini-' — without this, OpenCode Claude/Gemini routes (provider=openai) were misclassified as OpenAI-style and could leak xhigh past the new gate - effort.codex.test.ts: lock in the new exclusion with a regression test against the openai provider Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(effort): address reviewer feedback on xhigh effort + new models Closes the three P2 findings from PR Twigpine#1505 review: 1. Settings schema now accepts 'xhigh' so a persisted xhigh survives restart instead of being silently dropped by .catch(undefined). 2. ModelPicker /effort cycle is driven by getAvailableEffortLevels(model) instead of a boolean includeMax, so models supporting xhigh (opus-4-7/4-8, OpenAI/Codex) can actually select it from the picker. displayEffort clamp now uses the available levels list, so stale xhigh also clamps to high when the focused model doesn't support it. 3. SDK/control metadata uses getAvailableEffortLevels(model) instead of the EFFORT_LEVELS fallback that advertised xhigh to every max-capable model. SDK schema + generated types extended to include 'xhigh'. Also fixes a latent generator bug: the array case in generate-sdk-types now parenthesizes union/intersection elements so the trailing [] binds the whole type, e.g. ("a"|"b")[] rather than "a"|"b[]. Without this, the regenerated xhigh levels ended up typed as the single-literal "xhigh"[] and broke the modelInfo assignability check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(effort): order xhigh before max in EFFORT_LEVELS EFFORT_LEVELS now matches getAvailableEffortLevels() output order (['low', 'medium', 'high', 'xhigh', 'max']), and the order asserted by the existing effort.codex.test.ts tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(effort): order xhigh before max in settings + SDK schemas Matches the EFFORT_LEVELS / getAvailableEffortLevels order from the previous commit. The Zod enum order doesn't affect runtime validation, but keeps the source consistent and avoids confusion if anyone reads the enum literal to infer display order. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(effort): clamp ModelPicker selection and mark xhigh as current - ModelPicker.handleSelect: clamp the emitted/persisted effort to the focused model's available levels so a toggled-but-unsupported level (e.g. 'xhigh' on a model that doesn't support it) is never written to settings.json or handed to the consumer. Add focusedAvailableLevels + focusedDefaultEffort to the memo guard so the function regenerates when the focused model changes. - EffortPicker: compare the xhigh option against the persisted 'xhigh' level directly. The 'max' alias path is kept only for legacy settings.json values that still hold 'max' from before xhigh was introduced. * docs(effort): fix stale EffortPicker comment about xhigh normalization openAIEffortToStandard is a type cast that passes 'xhigh' through as a first-class EffortLevel — the shim only converts to 'max' at the Anthropic request boundary, not here. Update the comment to match. * docs(effort): update /effort help to match xhigh support matrix The /effort --help output still described max as "Opus 4.6 only" and xhigh as an "alias for max", but this PR promotes xhigh to a first-class EffortLevel and allows it for OpenCode Claude Opus 4.7/4.8 (with max also allowed for those Opus variants). Update the help so it matches the picker/runtime behavior: - max: "(Opus 4.6+)" - xhigh: "(OpenAI/Codex and Opus 4.7+)" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(sdk): address reviewer P2 — sync xhigh across override union, schemas, CLI - Add 'xhigh_effort' to ModelCapabilityOverride union so the new call at effort.ts:93 typechecks (P2 finding 1). - Add 'xhigh' to AgentDefinition.effort enum (coreSchemas.ts) and control.applied.effort enum (controlSchemas.ts), then regenerate coreTypes.generated.ts so the SDK public contract matches the first-class effort level (P2 finding 2). - Add 'xhigh' to the --effort CLI flag allowed list and help text (main.tsx:945-951) so users can actually pass --effort xhigh instead of hitting "It must be one of: low, medium, high, max". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(effort): narrow allowlist to shim-serialized models; sync max description Address reviewer findings on PR Twigpine#1505: P2: The broad `m.includes('opus-4') || m.includes('sonnet-4')` branch made older variants (claude-opus-4-1, claude-sonnet-4-5) advertise effort support, but the Anthropic /messages shim only serializes low/medium as anthropicBody.effort for the isAdaptive || isOpus45 set (opus-4-5/4-6/4-7/4-8, sonnet-4-6). For other models the shim only emits thinking for high/max, so low/medium on those models was silently dropped on the wire. Collapse the two 4-model branches into one that matches the shim's serialization set; the substring match still covers prefix variations (claude-, opencode-claude-). P3: getEffortLevelDescription('max') said "Opus 4.6 only" but modelSupportsMaxEffort now allows opus-4-6, opus-4-7, opus-4-8. Update the shared description to "Opus 4.6+" so the picker and /effort confirmation agree with the new support matrix (matching the /effort --help text from 3cf5de2). Add effort.codex.test.ts coverage: assert that opus-4-5/4-6/4-7/4-8 and sonnet-4-6 support effort, while opus-4-1, opus-4-2, and sonnet-4-5 do not (the latter three were previously true via the broad substring match and are now correctly excluded). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: trigger CodeRabbit re-review * fix(effort): gate modelSupportsXHighEffort on modelSupportsEffort --------- Co-authored-by: Gravirei <gravirei@users.noreply.github.com> Co-authored-by: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Expands the OpenCode Zen/Go integration with three new model registrations and adds
xhighas a first-class standard effort level, gated to only the models that actually accept it. Thexhighaddition is what unlocksclaude-opus-4-8for the picker — without it the new model would only exposelow/medium/high/max.New models
claude-opus-4-8/messagesmaxandxhigheffort.minimax-m3/messagesmimo-v2.5-free/chat/completionsModel count, description strings, README, and
docs/advanced-setup.mdare bumped to match (Zen 41→43, Go 12→13).Related: PR #1498 added
opencode-minimax-m3-freeto Zen. This PR is the Go counterpart of the M3 family plus two unrelated additions.New effort level:
xhigh(gated)Promotes
xhighfrom an OpenAI-only value to a first-classEffortLeveland adds amodelSupportsXHighEffortallowlist (mirrors the existingmodelSupportsMaxEffortpattern).xhighsurfaces only for:openai/codexprovider (existing behavior — they always hadxhighviaOPENAI_EFFORT_LEVELS).claude-opus-4-7andclaude-opus-4-8(and theopencode-prefixed variants).All other effort-supporting models — sonnet, haiku, claude-opus-4-6, 3P non-Claude models — do not see
xhighin the picker. A runtime clamp inresolveAppliedEffortalso downgrades a stale persistedxhightohighfor non-supporting models, so a leftoversettings.jsonvalue cannot surface as an API error.Implementation:
EFFORT_LEVELSgainsxhighas a 5th level.EffortLeveltype and Zod schemas (agent files, skill files, plugin agents/commands) now accept it.getAvailableEffortLevelsreturns[low, medium, high, xhigh, max]for max- and xhigh-capable models,[low, medium, high, xhigh]for OpenAI/Codex, and[low, medium, high]for everything else.xhighis ordered beforemaxto match the natural progression.toPersistableEffortandopenAIEffortToStandardpassxhighthrough unchanged. The previous normalization tomaxwas a persistence shim that no longer applies oncexhighis a valid value in its own right.claude-opus-4-8is added tomodelSupportsEffort,modelSupportsMaxEffort, andmodelSupportsXHighEffort.README updates
zai-defaultand document themodeloverride behavior (per-agentmodelcan differ from catalog default), with explicitbase_urlandapi_keykeys in the example.Test plan
bun test src/utils/effort.codex.test.ts src/integrations/gateways/opencode.test.ts src/integrations/index.test.ts src/integrations/compatibility.test.ts— all pass (75/75)bun run build— cleanbun run integrations:generate— succeedsmodelSupportsXHighEffortreturns true for opus-4-7, opus-4-8, and any model on theopenai/codexprovider; false for sonnet, haiku, and other Claude variantsgetAvailableEffortLevels(claude-opus-4-8)=[low, medium, high, xhigh, max]getAvailableEffortLevels(claude-sonnet-4-6)=[low, medium, high]resolveAppliedEffort(claude-sonnet-4-6, xhigh)clamps tohighclaude-opus-4-8in the model picker, pickxhighin/effort, confirm the request reaches OpenCode and returns a successful responseRisk
xhighis now scoped to a tight allowlist. There is no risk of a 1P or 3P user accidentally selectingxhighon a model that will reject it — the picker hides the option, and the runtime clamp catches stale persisted values.The Anthropic shim still maps
xhigh → maxat the request boundary, so wire format is unchanged for 1P models that do reach the API through other paths (no current path does, but the shim is left intact as a defense-in-depth).Files changed
11 files, +216/-37 across the branch. Latest commit adds the
modelSupportsXHighEffortgate and runtime clamp on top of the earlier xhigh work.Summary by CodeRabbit
New Features
UI / CLI
/efforthelp now show and acceptxhigh; selections clamp to supported levels.Documentation